#include "capfparser.h"

/**
 * parseAPF - parses arcanea property file
 * @param fileName
 * @param data
 * @param logPtr
 */
void CAPFParser::parseAPF(string fileName, CAPFNode &data, CLogger* logPtr) {

  // Assemble key-words
  map<string, int> keywords;
  keywords.insert(make_pair("{", TOKEN_CSTATBEGIN));
  keywords.insert(make_pair("}", TOKEN_CSTATEND));
  keywords.insert(make_pair("\"", TOKEN_STRQUOTATION));
  keywords.insert(make_pair("\t", TOKEN_TABULATOR));
  keywords.insert(make_pair("\n", TOKEN_EOLN));
  keywords.insert(make_pair(";", TOKEN_SEPARATOR));
  keywords.insert(make_pair("=", TOKEN_ASSIGNMENT));
  keywords.insert(make_pair("#", TOKEN_COMMENT));
  keywords.insert(make_pair(" ", TOKEN_WHITESPACE));

  // Assemble state manager for parser
  CParserStateManager m;
  m.addNextStateId(STATE_DEFAULT, TOKEN_EXPRESSION, STATE_AL);
  m.addNextStateId(STATE_AL, TOKEN_ASSIGNMENT, STATE_ASSIGNMENT);
  m.addNextStateId(STATE_AR, TOKEN_SEPARATOR, STATE_SEPARATOR);
  m.addNextStateId(STATE_CSB, TOKEN_CSTATEND, STATE_CSE);
  m.addNextStateId(STATE_CSB, TOKEN_EXPRESSION, STATE_AL);
  m.addNextStateId(STATE_CSE, TOKEN_EXPRESSION, STATE_AL);
  m.addNextStateId(STATE_CSE, TOKEN_CSTATEND, STATE_CSE);
  m.addNextStateId(STATE_SEPARATOR, TOKEN_EXPRESSION, STATE_AL);
  m.addNextStateId(STATE_SEPARATOR, TOKEN_CSTATEND, STATE_CSE);
  m.addNextStateId(STATE_ASSIGNMENT, TOKEN_EXPRESSION, STATE_AR);
  m.addNextStateId(STATE_ASSIGNMENT, TOKEN_CSTATBEGIN, STATE_CSB);
  m.addNextStateId(STATE_ASSIGNMENT, TOKEN_STRQUOTATION, STATE_STRINGB);
  m.addNextStateId(STATE_STRINGB, TOKEN_EXPRESSION, STATE_STRING);
  m.addNextStateId(STATE_STRING, TOKEN_STRQUOTATION, STATE_STRINGE);
  m.addNextStateId(STATE_STRINGE, TOKEN_SEPARATOR, STATE_SEPARATOR);
  m.setFirstStateId(STATE_DEFAULT);

  // Info
  if (logPtr) {
    logPtr->addInf("Entering method: " + getClassName() + "::parse");
    logPtr->addInf("Extracting tokens from file: " + fileName);
  }

  // Get tokens from file
  vector<CToken> tokens = CTokenizer::tokenize(fileName, keywords);

  // Info
  if (logPtr) {
    logPtr->addInf("Starting parsing tokens...");
  }

  // Parse file
  CAPFNode* tmp = &data;
  int state(m.getFirstStateId());
  processToken(tokens, tokens.begin(), state, m, &tmp, fileName, logPtr);

  // Info
  if (logPtr) {
    logPtr->addInf("Parsing done.");
  }
}
//---------------------------------------------------------------------------

/**
 * processToken - evaluates state depending on actual token
 * @oaram tokens
 * @param ti
 * @param state
 * @param sm
 * @param data
 * @param fileName
 * @param logPtr
 */
void CAPFParser::processToken(const vector<CToken> &tokens,
    vector<CToken>::const_iterator ti, int state, CParserStateManager &sm,
    CAPFNode** data, string fileName, CLogger* logPtr) {

  // If on end of the list, return
  if (ti == tokens.end()) {
    return;
  }

  // Find a next state and token, depending on thr actual state and token
  int nextState(sm.findNextStateId(state, (*ti).getId()));
  if (nextState != CParserStateManager::STATE_NOTFOUND) {
    parseToken(ti, nextState, data, logPtr);
    processToken(tokens, ++ti, nextState, sm, data, fileName, logPtr);
  } else if (logPtr) {
    logPtr->addErr("Token: '" + (*ti).getStr()
        + "' not expected! in file: '" + fileName + "' on line: "
        + CMath<int>::toString((*ti).getLine()));
    throw CUnexpectedTokenException(getClassName(), "processToken");
  }
}
//---------------------------------------------------------------------------

/**
 * parseToken - evaluates token
 * @param ti
 * @param state
 * @param data
 * @param logPtr
 */
void CAPFParser::parseToken(vector<CToken>::const_iterator ti, int state,
    CAPFNode** data, CLogger* logPtr) {

  // Switch token
  switch (state) {
    case STATE_STRING :
      parseStr(ti, data);
      break;
    case STATE_CSB :
      parseCSB(ti, data);
      break;
    case STATE_CSE :
      parseCSE(ti, data);
      break;
    case STATE_AL :
      parseAL(ti, data);
      break;
    case STATE_AR :
      parseAR(ti, data);
      break;
  }
}
//---------------------------------------------------------------------------

/**
 * parseCSB - parse beginning of compound statement
 * @param ti
 * @param data
 */
void CAPFParser::parseCSB(vector<CToken>::const_iterator ti, CAPFNode** data) {
  *data = (*data)->getLatestSubNodePtr();
}
//---------------------------------------------------------------------------

/**
 * parseCSE - parse ending of compound statement
 * @param ti
 * @param data
 */
void CAPFParser::parseCSE(vector<CToken>::const_iterator ti, CAPFNode** data) {
  *data = (*data)->getParentNodePtr();
}
//---------------------------------------------------------------------------

/**
 * parseAL - parse left side of assignment
 * @param ti
 * @param data
 */
void CAPFParser::parseAL(vector<CToken>::const_iterator ti, CAPFNode** data) {
  (*data)->createSubNode((*ti).getStr());
}
//---------------------------------------------------------------------------

/**
 * parseAR - parse right side of assignment
 * @param ti
 * @param data
 */
void CAPFParser::parseAR(vector<CToken>::const_iterator ti, CAPFNode** data) {
  (*data)->getLatestSubNodePtr()->setValue((*ti).getStr());
}
//---------------------------------------------------------------------------

/**
 * parseStr - parse string assignment
 * @param fileName
 * @param data
 * @param logPtr
 */
void CAPFParser::parseStr(vector<CToken>::const_iterator ti, CAPFNode** data) {
  (*data)->getLatestSubNodePtr()->setValue((*ti).getStr());
}
//---------------------------------------------------------------------------
